<!--
本组件实现自动上传多个图片文件到图书馆服务器的功能，常用于添加设备时上传多个图片
本组件定义的输出值的方法名为 @setUploadResult="getUploadResult($event)"
调用本组件时，父组件中定义一个名称为 getUploadResult(e) 的方法
e为本组件返回的上传结果，格式如下：
[
  'https://xzqtsg.net/api-public/file/image/{uuid}',
  'https://xzqtsg.net/api-public/file/image/{uuid}'
]
-->
<template>
  <div class="wrapper-uploader">
    <!--选择多图上传 Dialog-->
    <el-upload
      ref="theUploader"
      :auto-upload="true"
      :action="urlActionRemote"
      :data="paramsJson"
      :headers="headerJwt"
      :class="{ hide: flagHideUploader }"
      :show-file-list="true"
      :file-list="selectedFileList"
      list-type="picture-card"
      :limit="5"
      multiple
      :on-exceed="onExceed"
      :on-preview="onPreview"
      :on-change="onChange"
      :on-remove="onRemove"
      :before-upload="onBeforeUpload"
      :on-progress="onProgress"
      :on-success="onSuccess"
      :on-error="onError"
    >
      <el-icon><Plus /></el-icon>
    </el-upload>
    <!--图像预览 Dialog-->
    <el-dialog v-model="flagDialogPreview">
      <img style="width: 100%" :src="urlImagePreview" alt="" />
    </el-dialog>
  </div>
</template>

<script>
import { getAdminId } from '@/utils/common'
import { getJwtFromCookie } from '@/utils/jwt'
import { getLStorageObj } from '@/utils/web-storage'
import { validUploadFiles, isAllowedFile, isAllowedSizeByExt, getSizeLimitByExt } from '@/utils/file'
import { toastSuccess, toastError } from '@/utils/toast'
import { isBlankAry } from '@/utils/array'

import store from '@/store'
import { defineComponent, ref, onMounted, watch, computed } from 'vue'
export default defineComponent({
  name: 'UploadImagesCom',
  props: {},
  emit: ['update:setUploadResult'],
  setup(props, { emit }) {
    const uploadConfig = ref()
    // 设置显示图片的前缀
    const urlPrefixShowImg = ref(process.env.VUE_APP_URL_UPLOAD_SHOW_UUID)
    // 执行上传到图书馆服务器的action的url
    const urlActionRemote = ref(process.env.VUE_APP_URL_UPLOAD_ACTION)
    // 显示图片预览框
    const flagDialogPreview = ref(false)
    // 预览图片用到的变量
    const urlImagePreview = ref('')
    // 当选择满了图片后隐藏选择图片按钮要用到的判断变量
    const flagHideUploader = ref(false)
    // 上传成功后的服务器返回信息存到此变量中
    const uploadResultList = ref([])
    // 选中的文件列表，注意：这个变量不是双向绑定的，要在onChange方法里对其进行赋值
    const selectedFileList = ref([])

    const headerJwt = computed(() => {
      return { authorization: 'Bearer ' + getJwtFromCookie() }
    })
    const paramsJson = computed(() => {
      return { Id: 0, Type: 'Admin', FormData: null }
    })

    // 在onChange前执行，文件超出个数限制时的钩子，这个限制是 limit 属性设定的
    const onExceed = () => {
      // console.log('onExceed files', files)
      // console.log('onExceed fileList', fileList)
      // files 是选中的硬盘上的文件数组
      // fileList 是组件里已选中的文件数组
      // 已选中的文件数组长度+选中的硬盘上的文件数组的长度>limit属性值，才表示超出个数限制
      toastError('一次最多上传 5 个文件')
    }
    // 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用，移除文件时不会调用
    // 经过调试，选了3个文件，这个方法会执行3次
    // 要执行多次，所以验证文件不放在这里
    const onChange = (file, fileList) => {
      // console.log('onChange file', file)
      selectedFileList.value = validUploadFiles(fileList, uploadConfig.value.allowedExtImg, uploadConfig.value)
      // console.log('onchange selectedFileList', selectedFileList.value)
      flagHideUploader.value = fileList.length >= 5
    }
    const onProgress = () => {
      // clog('onProgress的event:', event)
      // clog('onProgress的file:', file)
      // 当前上传的进度
      // const loadProgress = Math.floor(event.percent)
    }
    // 自动上传时，在上传前进行格式校验和大小校验，注意选择了几个文件就会执行几次本方法
    const onBeforeUpload = (rawFile) => {
      console.log('onBeforeUpload() rawFile', rawFile)
      // 检查某个文件是否符合扩展名、文件大小
      if (!isAllowedFile(rawFile, uploadConfig.value.allowedExtImg)) {
        toastError('文件 [ ' + rawFile.name + ' ] 的格式不正确!')
        return false
      }
      if (!isAllowedSizeByExt(rawFile, uploadConfig.value)) {
        const msg =
          '文件 [ ' + rawFile.name + ' ] 的大小不能超过' + getSizeLimitByExt(rawFile, uploadConfig.value) + 'K!'
        toastError(msg)
        return false
      }
      return true
    }
    // 文件列表移除文件时的钩子，onBeforeUpload() 失败时也触发此方法
    const onRemove = (file, fileList) => {
      console.log('onRemove 时 file:', file)
      console.log('onRemove 时 fileList:', fileList)
      flagHideUploader.value = false
      if (file.response) {
        // 如果有response，说明是上传成功的图片，此时删除服务器上的文件
        store
          .dispatch('sys_ul_file/delByUuid', file.response.data.uuid)
          .then(() => {
            delByUuid(file.response.data.uuid)
            returnUploadResult(uploadResultList.value)
            toastSuccess('删除成功!')
          })
          .catch((error) => {
            console.log('服务器删除文件返回错误:', error)
          })
      }
    }
    // 预览文件
    const onPreview = (file) => {
      urlImagePreview.value = file.url
      flagDialogPreview.value = true
    }
    // 文件上传失败时的钩子
    const onError = (err) => {
      toastError(err)
      flagHideUploader.value = false
    }
    // 文件自动上传成功时的钩子，注意选择了几个文件就会执行几次本方法
    const onSuccess = (response, file) => {
      console.log('onSuccess() response', response)
      if (response && response.data.Message === 'OK') {
        // 上传成功
        flagHideUploader.value = true
        const uploadResult = response.data
        const returnObj = {
          uuid: '',
          url: '',
          urlThumb: '',
          urlWm: '',
        }
        returnObj.uuid = uploadResult.uuid
        // 上传成功，必有 Url，但是不一定有 UrlThumb 和 UrlWm
        returnObj.url = uploadResult.Url
        returnObj.urlThumb = uploadResult.UrlThumb
        returnObj.urlWm = uploadResult.UrlWm
        uploadResultList.value.push(returnObj)
        // if (uploadResult.UrlThumb && uploadResult.UrlThumb.length > 0) {
        //   returnObj.urlThumb = uploadResult.UrlThumb
        // }
        // if (uploadResult.UrlWm && uploadResult.UrlWm.length > 0) {
        //   returnObj.urlWm = uploadResult.UrlWm
        // }
        returnUploadResult(uploadResultList.value)
        toastSuccess('上传成功！')
      } else {
        if (response.data.message) {
          toastError(response.data.message)
        } else {
          toastError('服务器错误！上传' + file.name + '失败！')
        }
        // 上传失败，返回空数组
        returnUploadResult([])
      }
    }
    const returnUploadResult = (val) => {
      console.log('returnUploadResult 的 val：', val)
      // 调用本组件时，本组件实例添加一个方法 @setUploadResult="getUploadResult($event)"
      // 父组件中定义一个名称为 getUploadResult 的方法，方法的参数值就是这里返回的上传结果
      emit('setUploadResult', val)
    }

    const delByUuid = (uuid) => {
      for (let i = 0; i < uploadResultList.value.length; i++) {
        if ((uploadResultList.value[i].uuid = uuid)) {
          uploadResultList.value.splice(i, 1)
        }
      }
    }

    watch(
      () => props.selectedFile,
      (val) => {
        if (isBlankAry(val)) {
          flagHideUploader.value = false
          selectedFileList.value = []
          uploadResultList.value = []
        } else {
          if (val.length > 0 && val.length <= 5) {
            flagHideUploader.value = true
          }
          let obj = {}
          for (let i = 0; i < val.length; i++) {
            obj = {
              'uuid': val[i].uuid,
              'url': urlPrefixShowImg.value + val[i].uuid,
            }
            selectedFileList.value.push(obj)
          }
          uploadResultList.value = selectedFileList.value
        }
      },
      {
        deep: true,
        immediate: true,
      }
    )

    onMounted(() => {
      // 组件初始化时获得当前管理员id
      paramsJson.value.Id = getAdminId()
      // 组件初始化时获得当前上传文件的系统设置
      uploadConfig.value = getLStorageObj('UploadConfig')
    })

    return {
      uploadConfig,
      urlPrefixShowImg,
      urlActionRemote,
      flagDialogPreview,
      flagHideUploader,
      urlImagePreview,
      uploadResultList,
      selectedFileList,
      headerJwt,
      paramsJson,
      onExceed,
      onChange,
      onProgress,
      onRemove,
      onPreview,
      onError,
      onBeforeUpload,
      onSuccess,
      delByUuid,
      returnUploadResult,
    }
  },
})
</script>

<style lang="scss" scoped>
:deep(.el-upload--picture-card) {
  width: 60px;
  height: 60px;
  line-height: 60px;
  padding-top: 3px;
}
.hide {
  :deep(.el-upload--picture-card) {
    display: none;
  }
}
</style>
